<?php

namespace paymch\models;

use Yii;
use yii\base\Model;
use yii\db\Exception;
use common\models\trade\Trade;
use common\models\trade\TradeOrder;
use common\models\original\UserAccount;
use common\models\original\UserAccountLogs;
use common\helpers\Utils;

/**
 * 退款交易
 * AlipayForm is the model behind the alipay async notify form.
 * 
 * @author emhome <emhome@163.com>
 * @since 2.0
 */
class TradeRefund extends Model {

    /**
     * 交易中心分配给商户的应用ID
     * @var string(16) 交易中心分配给商户的应用ID
     * @example 72300007148
     * @static 是
     */
    public $app_id;

    /**
     * 签名类型
     * @var string(10) 商户生成签名字符串所使用的签名算法类型，目前支持RSA2和RSA，推荐使用RSA2
     * @example RSA2
     * @static 是
     */
    public $sign_type;

    /**
     * 请求签名
     * @var string(256) 请参考异步返回结果的验签
     * @example 601510b7970e52cc63db0f44997cf70e
     * @static 是
     */
    public $sign;

    /**
     * 编码格式
     * @var string(10) 编码格式，如utf-8、gbk、gb2312等
     * @example utf-8
     * @static 是
     */
    public $charset;

    /**
     * 接口版本
     * @var string(3) 调用的接口版本，固定为：1.0
     * @example 1.0
     * @static 是
     */
    public $version;

    /**
     * 买家在APP的登录账号的UID
     * @var string(16) 对应的APP的唯一用户号
     * @example 2088102122524333
     * @static 是
     */
    public $user_id;

    /**
     * 交易中心原始交易号
     * @var string(64) 外部商户订单号
     * @example 2013112011001004330000121536
     * @static 是
     */
    public $original_trade_no;

    /**
     * 外部商户订单号
     * @var string(64) 外部商户订单号
     * @example 2013112011001004330000121536
     * @static 是
     */
    public $bizno;

    /**
     * 交易使用的支付方式
     * @var string(16)  alipay|wechat|credit|apply
     * @example credit
     * @static 否
     */
    public $paymode;

    /**
     * 商户订单交易的总金额
     * @var decimal(9,2) 本次交易需支付的订单金额，单位为人民币（元）
     * @example 20
     * @static 是
     */
    public $total_amount;

    /**
     * 交易中选择使用的积分金额
     * @var string(10) 使用积分支付的金额，单位为积分单位（不使用积分为0）
     * @example 1200
     * @static 是
     */
    public $point_amount;

    /**
     * 商品订单描述
     * @var string(400) 该订单的备注、描述、明细等。对应请求时的body参数
     * @example 商品A内容
     * @static 否
     */
    public $body;
    //
    private $_partner = null;
    private $_originalTrade = null;
    private $_user = null;

    /**
     * 交易中心统一下单请求参数校验
     * @inheritdoc
     */
    public function rules() {
        return [
            [['sign_type', 'sign', 'charset', 'version', 'app_id', 'bizno', 'original_trade_no'], 'required'],
            [['sign_type', 'charset', 'version', 'app_id'], 'string', 'max' => 16],
            [['bizno', 'original_trade_no'], 'string', 'max' => 32],
            ['user_id', 'integer'],
            ['paymode', 'in', 'range' => ['alipay', 'wechat', 'credit', 'apple']],
            [['total_amount', 'point_amount'], 'number'],
            ['body', 'string', 'max' => 128],
            //校验APPID
            ['app_id', 'validateAppId'],
            //验证下单请求签名
            ['sign', 'validateSignature'],
            //验证原始交易是否存在
            ['original_trade_no', 'validateOriginalTrade'],
            //校验buyer_id
            ['user_id', 'validateUser'],
            //校验总价格
            ['total_amount', 'compare', 'compareValue' => 0, 'operator' => '>'],
            //校验积分账户
            ['point_amount', 'validatePointAmount'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels() {
        return [
            'app_id' => '交易中心分配给商户的应用ID',
            'charset' => '编码格式',
            'version' => '接口版本',
            'sign_type' => '签名类型',
            'sign' => '签名',
            'bizno' => '商户业务号',
            'original_trade_no' => '原始交易号',
            'user_id' => '管理员ID',
            'paymode' => '支付方式',
            'total_amount' => '订单金额',
            'point_amount' => '支付积分',
            'body' => '描述',
        ];
    }

    /**
     * Validates the APPID.
     * 验证app_id是否为该商户本身
     *
     * @param string $attribute the attribute currently being validated
     */
    public function validateAppId($attribute) {
        if (!$this->hasErrors()) {
            $partner = $this->getPartner();
            if (!$partner) {
                $this->addError($attribute, '未签约商户！');
            } else {
                $this->_partner = $partner;
            }
        }
    }

    /**
     * Validates the signature.
     * 校验请求的签名
     *
     * @param string $attribute the attribute currently being validated
     */
    public function validateSignature($attribute) {
        if (!$this->hasErrors()) {
            $paymchServer = new \common\components\paymch\MchServer([
                'appId' => $this->app_id,
                'version' => $this->version,
                'charset' => $this->charset,
                'rsaPublicKeyFilePath' => Yii::getAlias('@common') . '/data/keys/emchpay/rsa_public_key.pem',
            ]);
            $flag = $paymchServer->checkSignature($this->attributes, 'RSA2');
            if (!$flag) {
                $this->addError($attribute, '请求签名校验失败！');
            }
        }
    }

    /**
     * Validates the bizno.
     * 商户需要验证该通知数据中的bizno是否为商户系统中创建的订单号
     *
     * @param string $attribute the attribute currently being validated
     */
    public function validateOriginalTrade($attribute) {
        if (!$this->hasErrors()) {
            $originalTrade = $this->getOriginalTrade();
            if ($originalTrade == null) {
                $this->addError($attribute, '原始交易不存在或交易已完成关闭！');
            }
        }
    }

    /**
     * Validates the user_id.
     * 商户需要验证该通知数据中的buyer_id是否为商户系统中创建的订单号
     *
     * @param string $attribute the attribute currently being validated
     */
    public function validateUser($attribute) {
        if (!$this->hasErrors()) {
            $originalTrade = $this->getOriginalTrade();
            if ($originalTrade == null) {
                $this->addError($attribute, '原始交易不存在！');
            } elseif ($originalTrade->user_id != $this->$attribute) {
                $this->addError($attribute, '非法用户退款交易！');
            } else {
                $user = $this->getUser();
                if ($user == null) {
                    $this->addError($attribute, '未找到用户信息！');
                }
            }
        }
    }

    /**
     * Validates the buyer_id.
     * 商户需要验证该通知数据中的buyer_id是否为商户系统中创建的订单号
     *
     * @param string $attribute the attribute currently being validated
     */
    public function validatePointAmount($attribute) {

        if (!$this->hasErrors()) {
            if ($this->total_amount < $this->$attribute) {
                $this->addError($attribute, '退款积分不能大于退款总金额！');
            }
        }
    }

    /**
     * Finds trade by [[trade_no]]
     *
     * @return Trade|null
     */
    public function validateUnifiedTrade($attribute) {
        if (!$this->hasErrors()) {
            $ispayed = Trade::find()->where([
                'unified_no' => $this->$attribute,
                'balance' => Trade::BALANCE_PAYMENT,
                'status' => Trade::STATUS_SUCCESS
            ])->exists();
            if (!$ispayed) {
                $this->addError($attribute, '退款统一订单信息不存在！');
            }
        }
    }

    /**
     * Find Partner by [[appid]]
     *
     * @return \common\models\trade\Partner|null
     */
    protected function getPartner() {
        if ($this->_partner === null) {
            $this->_partner = \common\models\trade\Partner::findByAppId($this->app_id);
        }
        return $this->_partner;
    }

    /**
     * Find Partner by [[appid]]
     *
     * @return \common\models\trade\Partner|null
     */
    protected function getUser() {
        if ($this->_user === null) {
            $this->_user = \common\models\original\Users::findOne($this->user_id);
        }
        return $this->_user;
    }

    /**
     * Finds trade by [[trade_no]]
     *
     * @return Trade|null
     */
    protected function getOriginalTrade() {
        if ($this->_originalTrade === null) {
            $this->_originalTrade = Trade::find()->where([
                'trade_no' => $this->original_trade_no,
                'balance' => Trade::BALANCE_PAYMENT,
                'status' => Trade::STATUS_SUCCESS,
            ])->one();
        }
        return $this->_originalTrade;
    }

    /**
     * 关闭交易提交保存
     * @return bool whether the email was sent
     */
    public function save(&$msg) {
        if (!$this->validate()) {
            $msg = \common\helpers\Utils::arrayToString($this->firstErrors, false);
            return false;
        }

        $total = $this->total_amount;
        $cash = $this->total_amount - $this->point_amount;
        $credit = $this->point_amount;

        //查询退款申请的交易信息以及交易还剩余多少可以退款
        $originalTrade = $this->originalTrade;


        $unifiedTrades = TradeOrder::find()
        ->where([
            'original_no' => $this->original_trade_no,
            'status' => TradeOrder::STATUS_SUCCESS
        ])
        ->select(['SUM(pay*balance) as total', 'paymode'])
        ->groupBy('paymode')
        ->asArray()
        ->all();

        $amount = [
            'total' => 0,
            'cash' => 0,
            'credit' => 0,
        ];
        if (!empty($unifiedTrades)) {
            foreach ($unifiedTrades as $pay) {
                if ($pay['paymode'] == 'alipay' || $pay['paymode'] == 'wechat') {
                    $amount['cash'] += $pay['total'];
                } else {
                    $amount['credit'] += $pay['total'];
                }
                $amount['total'] += $pay['total'];
            }
        }

//        if (strval($amount['total']) < $total || strval($amount['credit']) < $credit || strval($amount['cash']) < $cash) { }
        if (bccomp($total, $amount['total'], 2) == 1 || bccomp($credit, $amount['credit'], 2) == 1 || bccomp($cash, $amount['cash'], 2) == 1) {
            $_print = [
                'bc' => [
                    'total' => bccomp($total, $amount['total'], 2),
                    'credit' => bccomp($credit, $amount['credit'], 2),
                    'cash' => bccomp($cash, $amount['cash'], 2),
                ],
                'amount' => $amount,
                'total' => $total,
                'credit' => $credit,
                'cash' => $cash,
            ];
            $msg = '超出退款金额限制！' . print_r($_print, true);
            return false;
        }


        //创建退款交易
        $trade = new Trade();
        $paymode = $this->paymode;
        $trade->generateTn($credit, $paymode, $this->partner->id);
        $tradeSuccess = true;
        $cashOrder = null;

        //开启事务
        $transaction = Yii::$app->db->beginTransaction();
        try {
            $trade->attributes = [
                'user_id' => $this->user_id,
                'paymode' => $paymode,
                'total' => $total,
                'cash' => $cash,
                'credit' => $credit,
                'partner_id' => $this->partner->id,
                'bizno' => $this->bizno,
                'subject' => $this->partner->name . '退款订单' . $this->bizno,
                'body' => $this->body,
                'balance' => Trade::BALANCE_REDUCE,
                'status' => Trade::STATUS_DEFAULT,
            ];

            if (!$trade->save()) {
                throw new Exception('退款交易创建失败！' . Utils::arrayToString($trade->errors, true));
            }

            if ($cash) {
                //创建现金交易
                $cashOrder = new TradeOrder();
                $cashOrder->attributes = [
                    'paymode' => $this->paymode,
                    'currency' => TradeOrder::CURRENCY_CNY,
                    'balance' => TradeOrder::BALANCE_REFUND,
                    'trade_id' => $trade->id,
                    'original_no' => $this->original_trade_no,
                    'pay' => $cash,
                    'status' => TradeOrder::STATUS_DEFAULT,
                ];
                if (!$cashOrder->save()) {
                    throw new Exception('退款现金交易创建失败！' . Utils::arrayToString($cashOrder->errors, true));
                }
                $tradeSuccess = false;
            }

            if ($credit) {
                //创建积分退款交易
                $creditOrder = new TradeOrder();
                $creditOrder->attributes = [
                    'trade_id' => $trade->id,
                    'original_no' => $this->original_trade_no,
                    'pay' => $credit,
                    'paymode' => TradeOrder::PAYMODE_CREDIT,
                    'currency' => TradeOrder::CURRENCY_CREDIT,
                    'balance' => TradeOrder::BALANCE_REFUND,
                    'status' => TradeOrder::STATUS_DEFAULT,
                ];
                if (!$creditOrder->save()) {
                    throw new Exception('退款交易积分支付失败！' . Utils::arrayToString($creditOrder->errors, true));
                }

                if ($tradeSuccess === true) {
                    //退积分
                    $account = UserAccount::findOne(['user_id' => $trade->user_id]);
                    $account->credit += strval($trade->credit * 10);
                    $account->updated_at = date('Y-m-d H:i:s');
                    if (!$account->save()) {
                        throw new Exception('账户积分退还失败！' . Utils::arrayToString($account->errors, true));
                    }
                    //创建积分日志
                    $creditLog = new UserAccountLogs();
                    $creditLog->attributes = [
                        'user_id' => $trade->user_id,
                        'type' => 'credit',
                        'amount' => $trade->credit * 10,
                        'current' => $account->credit,
                        'ip' => Yii::$app->request->userIP,
                        'mark' => '积分退款' . $trade->subject,
                        'detail' => 'trade_order',
                        'detail_id' => $creditOrder->id,
                    ];
                    if (!$creditLog->save()) {
                        throw new Exception('退款交易积分支付单号保存失败！' . Utils::arrayToString($creditLog->errors, true));
                    }
                    //积分支付
                    $creditOrder->pay_no = strval($creditLog->id);
                    $creditOrder->pay_time = $creditLog->input_at;
                    $creditOrder->status = TradeOrder::STATUS_SUCCESS;
                    if (!$creditOrder->save()) {
                        throw new Exception('退款交易积分支付单号保存失败！' . Utils::arrayToString($creditOrder->errors, true));
                    }
                }
            }

            if ($tradeSuccess === true) {
                $trade->status = Trade::STATUS_SUCCESS;
            } else {
                $trade->status = Trade::STATUS_WAITPAY;
            }
            if (!$trade->save()) {
                throw new Exception('交易状态变更失败！' . Utils::arrayToString($trade->errors, true));
            }
            $transaction->commit();
        } catch (Exception $e) {
            $transaction->rollBack();
            $msg = $e->getMessage();
            Yii::error($msg . "\n" . Utils::arrayToString($this->attributes), 'payment');
            return false;
        }

        //接口退款申请
        if ($cashOrder !== null) {
            RefundHandler::trade($cashOrder->id, $msg);
        }


        //通知业务订单成功了
        if ($trade->status == Trade::STATUS_SUCCESS) {
            $notify = new BusinessNotify($trade->id);
            $status = $notify->save($msg);
            if (!$status) {
                Yii::error($msg, 'payment');
            }
        }

        return true;
    }

}
